﻿#region File Description
/****************************************************************************
*                                                                           *
* Game Manager.                                                             *
*                                                                           *
* Copyright (c) Ionescu Marius. All rights reserved(2013).                  *
*                                                                           *
****************************************************************************/
#endregion
namespace Silverlight3D
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using System.Windows;

    public class VectorPolygon : VectorObject
    {
        public IList<Vector> Vectors { get; set; }

        public bool HideIfBackFacing { get; set; }

        public VectorPolygon()
        {
            this.Vectors = new List<Vector>();
            this.BrightnessFactor = 1.0;
        }

        public void AddVector(Vector vector)
        {
            Vectors.Add(vector);
        }

        public double AvgY
        {
            get
            {
                double avgY = 0;

                for (int i = 0; i < Vectors.Count; i++)
                {
                    avgY += Vectors[i].Y;
                }

                return avgY / Vectors.Count;
            }
        }

        public double AvgZ
        {
            get
            {
                double avgZ = 0;

                for (int i = 0; i < Vectors.Count; i++)
                {
                    avgZ += Vectors[i].Z;
                }

                return avgZ / Vectors.Count;
            }
        }

        public override double GetMaxZAfterRotation(Projector projector, Vector rotationCenter)
        {
            double maxZ = double.MinValue;

            for (int i=0; i< this.Vectors.Count; i++)
            {
                if (this.Vectors[i] != null)
                {
                    double z = projector.Rotate(this.Vectors[i], rotationCenter).Z;

                    if (z > maxZ)
                    {
                        maxZ = z;
                    }
                }
            }

            return maxZ;
        }

        public override Vector Center()
        {
            Vector result = new Vector();

            for (int i = 0; i < Vectors.Count; i++)
            {
                result = result + Vectors[i];
            }

            result.X = result.X / Vectors.Count;
            result.Y = result.Y / Vectors.Count;
            result.Z = result.Z / Vectors.Count;

            return result;
        }

        /// <summary>
        /// Only of use for polygons where all points are in the same 2D plane
        /// </summary>
        public Vector Normal
        {
            get
            {
                var side1 = Vectors[0] - Vectors[1];
                var side2 = Vectors[0] - Vectors[2];

                return VectorHelper.CrossProduct(side1, side2);
            }
        }

        public override UIElement Project(Projector projector, IShader shader, Vector rotationCenter)
        {
            return this.Project(projector, shader, null, rotationCenter);
        }

        public override UIElement Project(Projector projector, IShader shader, Vector viewPosition, Vector rotationCenter)
        {
            if (viewPosition == null)
            {
                viewPosition = projector.ViewPosition;
            }

            if (this.HideIfBackFacing && this.IsBackFacing(projector, rotationCenter))
            {
                return null;
            }

            var polygon = new Polygon();

            for (int i = 0; i < this.Vectors.Count; i++)
            {
                var point = projector.Project(Vectors[i], viewPosition, rotationCenter);

                if (point == null)
                {
                    return null;
                }

                polygon.Points.Add(point.Value);
            }

            double brightness = this.Shading(projector, rotationCenter);

            polygon.Stroke = shader.GetShadedBrush(this.StrokeColor, brightness);
            polygon.Fill = shader.GetShadedBrush(this.FillColor, brightness);

            polygon.UseLayoutRounding = true;

            return polygon;
        }

        public override VectorObject Clone()
        {
            var result = new VectorPolygon();

            for (int i = 0; i < Vectors.Count; i++)
            {
                result.AddVector(Vectors[i].Clone());
            }

            return result;
        }

        /// <summary>
        /// Whether the vector polygon is currently backfacing (after rotation).
        /// If rotationCenter is NULL, doesn't perform any rotation.
        /// </summary>
        /// <returns>True if vector polygon is backfacing.</returns>
        private bool IsBackFacing(Projector projector, Vector rotationCenter)
        {
            if (rotationCenter == null)
            {
                rotationCenter = new Vector(0, 0, 0);
            }

            var rotateVectorPolygon = projector.Rotate(this, rotationCenter);

            var side1 = rotateVectorPolygon.Vectors[0] - rotateVectorPolygon.Vectors[1];
            var side2 = rotateVectorPolygon.Vectors[0] - rotateVectorPolygon.Vectors[3];

            var normal = VectorHelper.CrossProduct(side1, side2);

            var vectorPolygonToViewPosition = projector.ViewPosition.AddZ(-projector.ScreenDistance) - rotateVectorPolygon.Vectors[0];

            var dotProduct = VectorHelper.DotProduct(normal, vectorPolygonToViewPosition);

            return dotProduct < 0;
        }

        /// <summary>
        /// Returns a value between 0 and 1, which can be used to shade vector polygons depending on its angle
        /// compared to the projector's light source position.
        /// </summary>
        public double Shading(Projector projector, Vector rotationCenter)
        {
            if (projector.LightSourcePosition == null)
            {
                return 1.0;
            }

            var rotateVectorPolygon = projector.Rotate(this, rotationCenter);

            double result = VectorHelper.DotProduct(rotateVectorPolygon.Normal, projector.LightSourcePosition - rotateVectorPolygon.Vectors[0]);

            if (result < 0)
            {
                result = 0;
            }

            result = (1 - this.BrightnessFactor) * result + this.BrightnessFactor;

            if (result > 1)
            {
                result = 1;
            }

            if (double.IsNaN(result))
            {
                result = 0;
            }

            return result;
        }
    }
}